home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
Other Langs
/
Tickle-4.0 (tcl)
/
src
/
tar_extract.c
< prev
next >
Wrap
Text File
|
1993-11-18
|
13KB
|
599 lines
#pragma segment TAR
/*
* Macintosh Tar
*
* Modified by Craig Ruff for use on the Macintosh.
*/
/*
* Extract files from a tar archive.
*
* Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
*
* @(#) extract.c 1.17 86/10/29 Public Domain - gnu
*/
#include "tar.h"
#include "stat.h"
#include <string.h>
extern union record *head; /* Points to current tape header */
extern struct
{
long st_size;
long st_mtime;
} hstat; /* Fake stat struct for compat. */
Boolean ExtractArchive();
extern void PrintHeader();
extern Boolean SkipFile();
int MakeDirs(); /* Makes required directories */
#ifdef TCLAPPL
/*
* Extract - extract the entire archive
*/
Extract() {
Point where;
SFReply reply;
char name[256];
extern WindowPtr theFeedbackWindow;
/*
* Use the standard file dialog to select the archive.
*/
where.h = where.v = 75;
MyGetFile(where, "\pTar Archive:", nil, -1, nil, nil, &reply);
if (!reply.good)
return;
/*
* Remember the VRefNum and Name for OpenArchive.
* Find out where to put the extracted files.
*/
arName = reply.fName;
WDDirVRef(reply.vRefNum, &arVRefNum, &arDirID);
if (! GetFolderPathName("Extraction Directory:", name, &dirVRefNum, &dirDirID))
return;
/*
* Extract and print the files as found in the archive.
*/
if (WindInit())
return;
/*TGE*/ UBegYield();
/*TGE*/ ShowFeedback();
/*TGE*/ SetPort(theFeedbackWindow);
TextFace(underline);
WPrintf(header);
/*TGE*/ SetPort(theFeedbackWindow);
TextFace(0);
ReadAnd(ExtractArchive);
CloseArchive();
WPrintf("--- tar extraction completed.");
WindEnd(autoPage);
/*TGE*/ UEndYield();
}
#endif
AEExtract(myFSS)
FSSpec *myFSS;
{
int myerr;
char name[256];
extern WindowPtr theFeedbackWindow;
#ifdef TCLAPPL
if (! GetFolderPathName("Extraction Directory:", name, &dirVRefNum, &dirDirID))
{
Feedback("Tar extract canceled.");
return noErr;
}
#else
dirDirID = myFSS->parID;
dirVRefNum = myFSS->vRefNum;
#endif
/*
* Extract and print the files as found in the archive.
*/
if (WindInit())
return;
/*
** Remember the VRefNum and Name for OpenArchive.
** Find out where to put the extracted files.
*/
arName = myFSS->name;
arDirID = myFSS->parID;
arVRefNum = myFSS->vRefNum;
UBegYield();
ShowFeedback();
SetPort(theFeedbackWindow);
TextFace(underline);
WPrintf(header);
SetPort(theFeedbackWindow);
TextFace(0);
ReadAnd(ExtractArchive);
CloseArchive();
WPrintf("--- tar extraction completed.");
WindEnd(autoPage);
UEndYield();
return noErr;
}
Cmd_Extract(clientData, interp, argc, argv)
char *clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
int arg_index;
short vref;
long dirid;
char pascal_name[256], *archive_name, *dir_name, *ptr;
struct stat arstat, dirstat;
char saveType[8], saveCreator[8], buf[8];
Boolean save_cvtNl = cvtNl;
#pragma unused (clientData)
extern WindowPtr theFeedbackWindow;
if (argc < 3)
{
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" ?options...? archive_filename extract_dirname\"", (char *) NULL);
return TCL_ERROR;
}
memcpy(saveType, fdType, 4);
memcpy(saveCreator, fdCreator, 4);
cvtNl = false;
for ( arg_index = 1 ; arg_index < argc ; ++arg_index )
{
if (argv[arg_index][0] != '-')
break;
if (argv[arg_index][1] == '-' && argv[arg_index][2] == '\0')
break;
if (argv[arg_index][1] == 't' && argv[arg_index][2] == '\0')
{
sprintf(buf, "%-4.4s", argv[arg_index+1]);
memcpy(fdType, buf, 4);
++arg_index;
}
else if (argv[arg_index][1] == 'c' && argv[arg_index][2] == '\0')
{
sprintf(buf, "%-4.4s", argv[arg_index+1]);
memcpy(fdCreator, buf, 4);
++arg_index;
}
else if (argv[arg_index][1] == 'a' && argv[arg_index][2] == '\0')
{
cvtNl = true;
}
else
{
Tcl_AppendResult(interp, "invalid option \"", argv[arg_index],
"\"", (char *) NULL);
memcpy(fdType, saveType, 4);
memcpy(fdCreator, saveCreator, 4);
cvtNl = save_cvtNl;
return TCL_ERROR;
}
}
if (arg_index >= argc)
{
Tcl_AppendResult(interp, "missing archive name argument", (char *) NULL);
memcpy(fdType, saveType, 4);
memcpy(fdCreator, saveCreator, 4);
cvtNl = save_cvtNl;
return TCL_ERROR;
}
archive_name = argv[arg_index++];
if (arg_index >= argc)
{
Tcl_AppendResult(interp, "missing extract directory argument", (char *) NULL);
memcpy(fdType, saveType, 4);
memcpy(fdCreator, saveCreator, 4);
cvtNl = save_cvtNl;
return TCL_ERROR;
}
if ( stat( archive_name, &arstat ) < 0)
{
Tcl_AppendResult(interp, "could not locate archive file \"", archive_name,
"\"", (char *) NULL);
memcpy(fdType, saveType, 4);
memcpy(fdCreator, saveCreator, 4);
cvtNl = save_cvtNl;
return TCL_ERROR;
}
dir_name = argv[arg_index++];
if ( stat( dir_name, &dirstat ) < 0)
{
Tcl_AppendResult(interp, "could not locate extract directory \"", dir_name,
"\"", (char *) NULL);
memcpy(fdType, saveType, 4);
memcpy(fdCreator, saveCreator, 4);
cvtNl = save_cvtNl;
return TCL_ERROR;
}
if ( ! S_ISDIR(dirstat.st_mode) )
{
Tcl_AppendResult(interp, "extract directory \"", dir_name,
"\" not a directory ", (char *) NULL);
memcpy(fdType, saveType, 4);
memcpy(fdCreator, saveCreator, 4);
cvtNl = save_cvtNl;
return TCL_ERROR;
}
dirVRefNum = dirstat.st_dev;
dirDirID = dirstat.st_ino;
/*
* Extract and print the files as found in the archive.
*/
WindInit();
arDirID = arstat.st_parid;
arVRefNum = arstat.st_dev;
ptr = strrchr(archive_name, ':');
if (ptr == NULL)
strcpy(pascal_name, archive_name);
else
strcpy(pascal_name, ptr + 1);
c2pstr(pascal_name);
arName = pascal_name;
tar_scripting = 1;
tar_interp = interp;
UBegYield();
SetPort(theFeedbackWindow);
TextFace(underline);
WPrintf(header);
SetPort(theFeedbackWindow);
TextFace(0);
ReadAnd(ExtractArchive);
CloseArchive();
WPrintf("--- tar extraction completed.");
WindEnd(autoPage);
UEndYield();
tar_scripting = 0;
tar_interp = NULL;
memcpy(fdType, saveType, 4);
memcpy(fdCreator, saveCreator, 4);
cvtNl = save_cvtNl;
return TCL_OK;
}
/*
* Extract a file from the archive.
*/
Boolean
ExtractArchive()
{
register char *data;
register char *p;
union record *rec;
int i, namelen;
Boolean errFound = false;
long check, written;
long size;
OSErr err;
char macName[256];
HParamBlockRec dpb;
HParamBlockRec fpb;
char *routine = "\pExtractArchive";
SaveRec(&head); /* Make sure it sticks around */
UseRec(head); /* And go past it in the archive */
DecodeHeader(head, &hstat, 1); /* Snarf fields */
/* Print the record from 'head' and 'hstat' */
PrintHeader();
switch (head->header.linkflag) {
default:
WPrintf("Unknown file type %d for %s",
head->header.linkflag, head->header.name);
break;
case LF_OLDNORMAL:
case LF_NORMAL:
/*
* Appears to be a file.
* See if it's really a directory.
*/
namelen = strlen(head->header.name) - 1;
if (head->header.name[namelen] == '/')
goto really_dir;
FixName(head->header.name, macName);
again_file:
fpb.fileParam.ioCompletion = nil;
fpb.fileParam.ioNamePtr = macName;
fpb.fileParam.ioVRefNum = dirVRefNum;
fpb.fileParam.ioFVersNum = 0;
fpb.fileParam.ioDirID = dirDirID;
err = PBHCreate(&fpb, false);
if (err == noErr) {
fpb.fileParam.ioCompletion = nil;
fpb.fileParam.ioNamePtr = macName;
fpb.fileParam.ioVRefNum = dirVRefNum;
fpb.fileParam.ioDirID = dirDirID;
fpb.fileParam.ioFVersNum = 0;
fpb.fileParam.ioFDirIndex = 0;
if (PBHGetFInfo(&fpb, false)) {
OSAlert(routine, "\pPBHGetFInfo", macName,
fpb.fileParam.ioResult);
goto doNext;
}
strncpy((char *)&fpb.fileParam.ioFlFndrInfo.fdCreator, fdCreator, 4);
strncpy((char *)&fpb.fileParam.ioFlFndrInfo.fdType, fdType, 4);
fpb.fileParam.ioCompletion = nil;
fpb.fileParam.ioNamePtr = macName;
fpb.fileParam.ioVRefNum = dirVRefNum;
fpb.fileParam.ioDirID = dirDirID;
fpb.fileParam.ioFVersNum = 0;
if (PBHSetFInfo(&fpb, false)) {
OSAlert(routine, "\pPBHSetFInfo", macName,
fpb.fileParam.ioResult);
goto doNext;
}
fpb.ioParam.ioPermssn = fsWrPerm;
fpb.ioParam.ioMisc = nil;
err = PBHOpen(&fpb, false);
}
if (err != noErr) {
if (MakeDirs(macName))
goto again_file;
PgmAlert(routine, "\pCould not make file", macName);
errFound = SkipFile((long)hstat.st_size);
break;
}
/*
* Note that this only extracts data forks!
*/
for (size = hstat.st_size;
size > 0;
size -= written) {
/*
* Locate data, determine max length
* writeable, write it, record that
* we have used the data, then check
* if the write worked.
*/
if ((rec = FindRec()) == nil)
return(true);
data = rec->charptr;
written = EndOfRecs()->charptr - data;
if (written > size)
written = size;
/*
* Convert newlines to return for Mac compatability.
*/
if (cvtNl) {
for (i = written, p = data; --i >= 0; p++)
if (*p == LF)
*p = RETURN;
}
check = written;
fpb.ioParam.ioBuffer = data;
fpb.ioParam.ioReqCount = check;
fpb.ioParam.ioPosMode = fsAtMark;
fpb.ioParam.ioPosOffset = 0;
err = PBWrite((ParmBlkPtr) &fpb, false);
if (err != noErr) {
OSAlert(routine, "\pPBWrite", macName, err);
goto doNext;
}
check = fpb.ioParam.ioActCount;
/*
* The following is in violation of strict
* typing, since the arg to userec
* should be a struct rec *. FIXME.
*/
UseRec(data + written - 1);
if (check == written)
continue;
/*
* Error in writing to file.
* Print it, skip to next file in archive.
*/
PgmAlert(routine, "\pWrite short", macName);
doNext:
PBClose((ParmBlkPtr) &fpb, false);
errFound = SkipFile((long)(size - written));
goto quit;
}
PBClose((ParmBlkPtr) &fpb, false);
quit:
break;
case LF_DIR:
/* Check for trailing / */
namelen = strlen(head->header.name)-1;
really_dir:
while (namelen && head->header.name[namelen] == '/')
head->header.name[namelen--] = '\0'; /* Zap / */
FixName(head->header.name, macName);
/* FALL THROUGH */
again_dir:
dpb.fileParam.ioCompletion = nil;
dpb.fileParam.ioNamePtr = macName;
dpb.fileParam.ioVRefNum = dirVRefNum;
dpb.fileParam.ioFVersNum = 0;
dpb.fileParam.ioDirID = dirDirID;
err = PBDirCreate(&dpb, false);
if ((err != noErr) && (err != dupFNErr)) {
if (MakeDirs(macName))
goto again_dir;
PgmAlert(routine, "\pCould not make directory", macName);
}
break;
}
/* We don't need to save it any longer. */
SaveRec((union record **) 0);
return(errFound);
}
/*
* After a file/link/symlink/dir creation has failed, see if
* it's because some required directory was not present, and if
* so, create all required dirs.
*/
int
MakeDirs(pathname)
char *pathname;
{
int madeone = 0; /* Did we do anything yet? */
int i, savedLen;
OSErr err;
HParamBlockRec pb;
savedLen = pathname[0] & 0xff;
/*
* skip initial ':'
*
* Note that the directory name has already been converted to Mac style.
*/
for (i = 2; i < savedLen; i++) {
while ((i < savedLen) && (pathname[i] != ':'))
i++;
if (i == savedLen)
break;
pathname[0] = i++ - 1;
pb.fileParam.ioCompletion = nil;
pb.fileParam.ioNamePtr = pathname;
pb.fileParam.ioVRefNum = dirVRefNum;
pb.fileParam.ioFVersNum = 0;
pb.fileParam.ioDirID = dirDirID;
err = PBDirCreate(&pb, false);
if (err == dupFNErr) {
continue;
} else if (err != noErr) {
OSAlert("\pMakeDirs", "\pPBDirCreate", pathname,
pb.fileParam.ioResult);
return(0);
} else {
madeone++; /* Remember if we made one */
continue;
}
}
pathname[0] = savedLen;
return(madeone); /* Tell them to retry if we made one */
}
/*
* FixName - convert to a Mac style pathname
*
* Conversions:
* . -> (Stay at this directory level)
* .. -> :: (Up a directory level)
* .xxxx -> _xxxx (Don't get in trouble with device names)
* xx:xx -> xx/xx (Don't get in trouble with directory delims)
*/
FixName(tar, mac)
register char *tar;
char *mac;
{
char *end = tar + strlen(tar);
register char *p = mac + 1;
register char *next;
for (next = tar; tar < end; next++) {
/*
* Swallow up all contiguous '/' characters.
*/
while (*next && (*next == '/'))
next++;
/*
* Find the entire name up until the next '/'.
*/
tar = next;
while (*next && (*next != '/'))
next++;
*next = 0;
*p++ = ':';
if (*tar == '.')
switch (*(tar + 1)) {
case '\0':
p--;
continue;
case '.':
if (*(tar + 2) == 0)
continue;
/* else FALL THROUGH */
default:
*tar = '_';
}
while (tar < next) {
if (*tar == ':')
*tar = '/';
*p++ = *tar++;
}
}
*mac = p - mac - 1;
}